home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / editors / mutt / me2s_pl7.zoo / mu_edit2 / util / canonize.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-05  |  11.3 KB  |  418 lines

  1. static char rcsid[] = "$Id: canonize.c,v 1.3 1992/11/10 03:14:34 mike Exp $";
  2.  
  3. /* $Log: canonize.c,v $
  4.  * Revision 1.3  1992/11/10  03:14:34  mike
  5.  * - `canonize' now works for path names, too.
  6.  *
  7.  * Revision 1.2  1992/09/07  00:33:52  mike
  8.  * - Rewrote `canonize' completely for the ATARI version.
  9.  *
  10.  * Revision 1.1  1992/09/06  19:31:32  mike
  11.  * Initial revision
  12.  *
  13.  */
  14.  
  15. /*
  16.  * Name: canonize
  17.  * Purpose:
  18.  *  Calculate a file's unique (canonical) pathname, suitable for comparing
  19.  *    with other canonical names.
  20.  * Usage: char *canonize(fname,cname, curdir);
  21.  * Input:
  22.  *   char *fname;  Pointer to file name to canonize.
  23.  *   char *cname;  Where to put the canonical name.
  24.  *   char *curdir; Pointer to the name of the current directory.  Pass in
  25.  *           (char *)NULL if you want me to query the OS for this
  26.  *           info (the normal case).
  27.  * Returns:
  28.  *   Pointer to canonical name, or NULL if file name was illegal.
  29.  * Notes:
  30.  *   On Unix, getcwd() is VERY slow.  If you call canonize() very often,
  31.  *     you'll want to call getcwd() once, save the dir and pass it in via
  32.  *     curdir.
  33.  *   No wildcards in fname - see fxpand().
  34.  *   MS-DOS
  35.  *     "A:" => "A:<current directory>"
  36.  *     The canonical name will only contain "/"s.  No "\"s.
  37.  *     Will parse both "\" and "/".
  38.  *     The drive letter is uppercase, everything else is lowercase.
  39.  *   ATARIST (from jwahar r.  bammi (bammi@cadence.com))
  40.  *     Usage just like Unix.  The library getcwd will canonize, and will be
  41.  *     consistent (especially) inspite of unixmode.  Also getcwd() is not
  42.  *     expensive on the atari, so we use it.
  43.  *   Every file has exactly one, unique canonical name.  Canonical names may
  44.  *     be compared to determine if two names specify the same file.
  45.  *   The format for canonical names is:  C:/path/FILE.EXT or C:/path/FILE
  46.  *     for files lacking an extension.  Note that the drive name is
  47.  *     always included, a complete path name (starting at the root) is
  48.  *     provided and references to "."  and ".."  are removed.
  49.  *   Leading blanks in file name are ignored and dropped from the
  50.  *     canonical name.
  51.  *   On Domain/OS (Apollo), 2 leading slashes (//foo) is very different
  52.  *     from a single leading slash.  Yech.  "Normal" Unix doesn't care if
  53.  *     there is one or more leading slashes.  Also, "//foo/.."  => "//".
  54.  *     "/.."  => "//" but I don't know if I care enough to make this
  55.  *     work.
  56.  * Defects:
  57.  *   Character device names are not recognized, and so they are treated
  58.  *     like ordinary files.
  59.  *   Error checking is not very robust.
  60.  * WARNING
  61.  *   This routine is a classic bomb waiting to go off.  It fills a fixed
  62.  *     length string (of unknown length) with text of unknown lengths.  One
  63.  *     of these days it will write off the end.
  64.  *   Also, the routine getcwd() is used poorly.  I should do getter error
  65.  *     checking and give it a better chance of working with a long path name.
  66.  *   The real fix for this is to use dynamic strings.  Until then, pass in a
  67.  *     big buffer.
  68.  * System:
  69.  *   MS-DOS 2.1 and up. Lattice C
  70.  *   With minor mods:  Microsoft C
  71.  *   Unix
  72.  *   Domain/OS (mostly)
  73.  * Author:    Jim Conrad    06/08/86
  74.  * Rewritten:    Craig Durland    9/86, 6/89
  75.  * Modified:  11/91
  76.  */
  77.  
  78. /* Copyright 1986 Craig Durland
  79.  *   Distributed under the terms of the GNU General Public License.
  80.  *   Distributed "as is", without warranties of any kind, but comments,
  81.  *     suggestions and bug reports are welcome.
  82.  */
  83.  
  84. #include <stdio.h>
  85. #include <os.h>
  86. #include <const.h>
  87. #include <char.h>    /* <char.h> or <ctype.h> */
  88.  
  89. extern char *getcwd();
  90.  
  91. static char *fnptr, *cnptr;        /* !!! not reentrent */
  92. static int get_dpart();
  93.  
  94.     /* flags for get_dpart() */
  95. #define TEXT    0
  96. #define PARENT    1
  97. #define DONE    2
  98. #define ZILCH    3
  99.  
  100.  
  101. #ifdef ATARI
  102.  
  103. /*
  104.  *    Strip off the last dir name of a path.
  105.  *    `p' points to the byte after the last valid
  106.  *    char in the path, not the slash.
  107.  */
  108.  
  109.  
  110. static char *strip_last_dir(p,cname)
  111. register char *p;
  112. register char *cname;
  113. {
  114.   while (--p >= cname)
  115.     if (*p == U_SLASH)
  116.       return p + 1;
  117.   return NULL;
  118. }
  119.  
  120.  
  121.  
  122. char *canonize(fname,cname,curdir)
  123. char *fname;
  124. char *cname;
  125. char *curdir;
  126. {
  127.   register char *cp;
  128.   register char *fp;
  129.            int   prefix;
  130.  
  131.   cp = cname;
  132.   fp = fname;
  133.   /*
  134.    *    See if `fname' contains a path prefix.
  135.    */
  136.   prefix = 0;
  137.   if (fp[0] == M_SLASH || fp[0] == U_SLASH)
  138.   {
  139.     if (tolower(fp[1]) == 'd' && tolower(fp[2]) == 'e' && tolower(fp[3]) == 'v'
  140.         && (fp[4] == M_SLASH || fp[4] == U_SLASH) && isalpha(fp[5])
  141.     && (fp[6] == M_SLASH || fp[6] == U_SLASH))
  142.       prefix = 1;
  143.     else
  144.       prefix = 2;
  145.   }
  146.   else if (fp[1] == ':')
  147.     prefix = 3;
  148.   else if (fp[0] == '.')
  149.   {
  150.     if (fp[1] == M_SLASH || fp[1] == U_SLASH || fp[1] == '\0')
  151.       prefix = 4;
  152.     else if (fp[1] == '.' && (fp[2] == M_SLASH || fp[2] == U_SLASH || fp[2] == '\0'))
  153.       prefix = 5;
  154.   }
  155.   switch (prefix)
  156.   {
  157.     case 0 :
  158.       /*
  159.        *    No path prefix.
  160.        */
  161.       if (curdir == NULL)
  162.       {
  163.         if (getcwd(cp,250) == NULL)
  164.           return NULL;
  165.       }
  166.       else
  167.         strcpy(cp,curdir);
  168.       while (*cp++);
  169.       --cp;
  170.       if (cp[-1] != U_SLASH)
  171.         *cp++ = U_SLASH;
  172.       strcpy(cp,fp);
  173.       return cname;
  174.     case 1 :
  175.       /*
  176.        *    A path of the form /dev/x....
  177.        */
  178.       *cp++ = tolower(fp[5]);
  179.       *cp++ = ':';
  180.       *cp++ = U_SLASH;
  181.       fp += 7;
  182.       break;
  183.     case 2 :
  184.       /*
  185.        *    A path starting with a slash.
  186.        */
  187.       if (curdir == NULL)
  188.       {
  189.         if (getcwd(cp,250) == NULL)
  190.           return NULL;
  191.       }
  192.       else
  193.         strcpy(cp,curdir);
  194.       cp += 3;
  195.       fp++;
  196.       break;
  197.     case 3 :
  198.       /*
  199.        *    A path starting with a drive id and a colon.
  200.        */
  201.       *cp++ = tolower(*fp++);
  202.       *cp++ = *fp++;
  203.       *cp++ = U_SLASH;
  204.       fp++;
  205.       break;
  206.     case 4 :
  207.       /*
  208.        *    A path starting with a dot.
  209.        */
  210.       if (curdir == NULL)
  211.       {
  212.         if (getcwd(cp,250) == NULL)
  213.           return NULL;
  214.       }
  215.       else
  216.         strcpy(cp,curdir);
  217.       while (*cp++);
  218.       --cp;
  219.       if (fp[1] == M_SLASH || fp[1] == U_SLASH)
  220.       {
  221.         *cp++ = U_SLASH;
  222.         fp += 2;
  223.       }
  224.       else
  225.         fp++;
  226.       break;
  227.     case 5 :
  228.       /*
  229.        *    A path starting with two dots.
  230.        */
  231.       if (curdir == NULL)
  232.       {
  233.         if (getcwd(cp,250) == NULL)
  234.           return NULL;
  235.       }
  236.       else
  237.         strcpy(cp,curdir);
  238.       while (*cp++);
  239.       --cp;
  240.       if ((cp = strip_last_dir(cp,cname)) == NULL)
  241.         return NULL;
  242.       if (fp[2] == M_SLASH || fp[2] == U_SLASH)
  243.         fp += 3;
  244.       else
  245.         fp += 2;
  246.   }
  247.   /*
  248.    *    At this point we got the drive id, the colon and the
  249.    *    slash, `fp' points to the first directory in the remaining
  250.    *    path or to the filename.
  251.    */
  252.   while (*fp)
  253.   {
  254.     switch (*fp)
  255.     {
  256.       case M_SLASH :
  257.       case U_SLASH :
  258.         *cp++ = U_SLASH;
  259.         if (*fp++ == '.')
  260.         {
  261.           if (fp[1] = '.' && (fp[2] == M_SLASH || fp[2] == U_SLASH))
  262.           {
  263.             if ((cp = strip_last_dir(cp - 1, cname)) == NULL)
  264.               return NULL;
  265.             fp += 3;
  266.           }
  267.           else if (fp[1] == M_SLASH || fp[1] == U_SLASH)
  268.             fp += 2;
  269.         }
  270.         break;
  271.       default :
  272.         *cp++ = tolower(*fp++);
  273.     }
  274.   }
  275.   *cp = '\0';
  276.   if (cp == cname)
  277.     return NULL;
  278.   return cname;
  279. }
  280.  
  281. #else
  282.  
  283.         /* convert fname to a full file spec */
  284. char *canonize(fname,cname, curdir) char *fname; char *cname, *curdir;
  285. {
  286.   register char *first_slash;
  287.  
  288.   fnptr = fname; cnptr = cname;
  289.  
  290.   while (isspace(*fnptr)) fnptr++;    /* Ignore leading white space */
  291.  
  292.     /* Ensure file name is not empty ie contains at least "c\0" */
  293.   if (*fnptr == '\0') return NULL;
  294.  
  295. #if MSDOZ
  296.   if (fnptr[1] == ':')        /* if drive provided, use it */
  297.     { *cnptr++ = toupper(*fnptr); fnptr += 2; }
  298.   else *cnptr++ = 'A' +getdsk();    /* drive missing: use current drive */
  299.   *cnptr++ = ':';
  300. #endif    /* MSDOZ */
  301.  
  302.   first_slash = cnptr;                /* point to opening slash */
  303.   if (ISSLASH(*fnptr))
  304.   {
  305.     *cnptr++ = U_SLASH;
  306. #if DOMAIN_OS        /* just for Domain/OS.  Yech */
  307. {
  308.     char c;
  309.     /* Check to see if fname starts with // (and only //) */
  310.     if (ISSLASH(fnptr[1]))
  311.       if ((c = fnptr[2]) == '\0' || !ISSLASH(c))
  312.     { fnptr++; *cnptr++ = U_SLASH; first_slash++; }
  313. }
  314. #endif    /* DOMAIN_OS */
  315.   }
  316.   else            /* No leading "/" => path name not anchored */
  317.   {            /* so fname is relative to current directory */
  318. #if MSDOZ
  319.     *cnptr++ = U_SLASH;            /* tack on a leading slash */
  320.     if (getcd(*cname -'A' +1,cnptr)) return NULL;
  321.     if (*cnptr)            /* if current dir != "" (ie the root) */
  322.     {        /* skip over dir, switching slashes as we go along */
  323.       for (; *cnptr; cnptr++) if (*cnptr == M_SLASH) *cnptr = U_SLASH; 
  324.       *cnptr++ = U_SLASH;
  325.     }
  326. #else
  327.  
  328. #if UX_OS
  329.     if (curdir) strcpy(cnptr,curdir);        /* better be canonized */
  330.     else if (getcwd(cnptr,250) == NULL) return NULL;    /* at least a "/" */
  331.     while (*cnptr) cnptr++;        /* move to end of current directory */
  332.     if (cnptr[-1] != U_SLASH) *cnptr++ = U_SLASH;   /* in case cd is "/" */
  333. #else
  334.  
  335. #if ATARI
  336.     if (getcwd(cnptr,250) == NULL) return NULL;        /* at least a "/" */
  337. #if defined(__MINT__)     /* just in case */
  338.     for (; *cnptr; cnptr++) if (*cnptr == M_SLASH) *cnptr = U_SLASH; 
  339. #else
  340.     while (*cnptr) cnptr++;        /* move to end of current directory */
  341. #endif    /* __MINT__ */
  342.     if (cnptr[-1] != U_SLASH) *cnptr++ = U_SLASH;   /* in case cd is "/" */
  343. #else
  344.  
  345.     /* Nothing defined, nuke the compile */
  346.     Need_to_define = Something;
  347. #endif  /* ATARI */
  348. #endif  /* UX_OS */
  349. #endif    /* MSDOZ */
  350.   }
  351.  
  352.   while (TRUE)
  353.   {
  354.     switch (get_dpart())
  355.     {
  356.       case DONE:   *cnptr = '\0'; goto done;
  357.       case PARENT: 
  358.     if (--cnptr != first_slash) while (*--cnptr != U_SLASH) ;
  359.     /* now tack on a slash after the name */
  360.       case TEXT: *cnptr++ = U_SLASH; break;
  361.     }
  362.   }
  363. done:
  364.   if (--cnptr != first_slash) *cnptr = '\0';  /* get rid of trailing slash */
  365.  
  366. #if MSDOZ
  367.   lowercase(first_slash);    /* lowercase all but drive */
  368. #endif    /* MSDOZ */
  369.   return cname;
  370. }
  371.  
  372.     /* know: cnptr[-1] == slash */
  373. static int get_dpart()
  374. {
  375.   register char *ptr = cnptr;
  376.   register char c1, c2;
  377.  
  378.   if (*fnptr == '.')        /* check for /. or /.. */
  379.   {
  380.     fnptr++;            /* point to "/", "." or "" */
  381.     c1 = fnptr[0];
  382.     if (!(ISSLASH(c1) || c1 == '\0'))        /* ignore "/./" or "/." */
  383.     {
  384.       c2 = fnptr[1];
  385.       if (c1 == '.' && (ISSLASH(c2) || c2 == '\0'))    /* "/../" or "/.." */
  386.     { fnptr++; return PARENT; }
  387.       *cnptr++ = '.';                /* false alarm */
  388.     }
  389.   }
  390.   while (*fnptr)
  391.   {
  392.     if (ISSLASH(*fnptr)) { fnptr++; break; }
  393.     *cnptr++ = *fnptr++;
  394.   }
  395.   if (cnptr != ptr)   return TEXT;
  396.   if (*fnptr == '\0') return DONE;
  397.   return ZILCH;        /* probably something like multiple slashes */
  398. }
  399.  
  400. #endif /* ATARI */
  401.  
  402.  
  403.  
  404. #ifdef TEST
  405. /* ******************************************************************** */
  406. /* *************** Test *********************************************** */
  407. /* ******************************************************************** */
  408.  
  409. main()
  410. {
  411.   char buf[80], cname[512];
  412.  
  413.   printf("File name to canonize: "); gets(buf);
  414.   if (NULL == canonize(buf,cname, (char *)NULL)) printf("Bleech\n");
  415.   else printf(">%s<\n>%s<\n",buf,cname);
  416. }
  417. #endif
  418.